using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Prism.Commands;
using Prism.Ioc;
using Prism.Navigation;
using Prism.Services.Dialogs;
using PrismPractice.Const;
using WPFTemplateLib.Controls.WpfToast;
using WPFTemplateLib.WpfHelpers;

namespace PrismPractice.Core.Mvvm
{
	public abstract class ViewModelBase : BusinessBaseViewModel, IDestructible, INotifyDataErrorInfo
	{
		#region 成员构造

		public Dispatcher Dispatcher => Application.Current?.Dispatcher;
		protected IDialogService DialogService { get; private set; }

		protected ViewModelBase(string name) : base(name)
		{
			//DialogService = ContainerLocator.Current.Resolve<IDialogService>();
			//使用自定义的对话框服务
			DialogService = ContainerLocator.Current.Resolve<IDialogService>(ServiceNames.MY_DIALOG_SERVICE);
		}

		public virtual void Destroy() { }

		#endregion

		#region [命令] 弹窗
		private DelegateCommand<string> _ShowDialogCmd;
		public DelegateCommand<string> ShowDialogCmd => _ShowDialogCmd ??= new DelegateCommand<string>(ExecuteShowDialogCmd);
		private void ExecuteShowDialogCmd(string viewName)
		{
			try
			{
				DialogService.ShowDialog(viewName);
			}
			catch(ContainerResolutionException ex)
			{
				MessageBox.Show($"页面解析错误，请检查是否存在该页面[{viewName}]：{ex}");
			}
			catch(Exception ex)
			{
				MessageBox.Show($"{ex}");
			}
		}
		#endregion

		#region 验证

		/// <summary>
		/// 错误列表
		/// </summary>
		private Dictionary<string, List<string>> _Errors = new Dictionary<string, List<string>>();

		private void SetErrors(string propertyName, List<string> propertyErrors)
		{
			//clear any errors that already exist for this property.
			_Errors.Remove(propertyName);

			//Add the list collection for the specified property.
			_Errors.Add(propertyName, propertyErrors);

			//Raise the error-notification event.
			if(ErrorsChanged != null)
				ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
		}

		private void ClearErrors(string propertyName)
		{
			//Remove the error list for this property.
			_Errors.Remove(propertyName);

			//Raise the error-notification event.
			if(ErrorsChanged != null)
				ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
		}

		/// <summary>
		/// 是否包含错误
		/// </summary>
		/// <param name="propertyName">属性名</param>
		public bool IsContainErrors(string propertyName)
		{
			return (GetErrors(propertyName) as List<string>)?.Count > 0;
		}

		/// <summary>
		/// 是否包含错误
		/// </summary>
		/// <param name="propertyName">属性名列表</param>
		public bool IsContainErrors(List<string> propertyNameList)
		{
			return propertyNameList.Exists(x => IsContainErrors(x));
		}

		/// <summary>
		/// 获取给定属性列表的错误列表（参数传空则获取所有错误列表）
		/// </summary>
		/// <param name="propertyNameList">属性名列表</param>
		/// <returns>错误列表（List＜string＞）</returns>
		public List<string> GetErrors(List<string> propertyNameList)
		{
			if(!propertyNameList?.Any() ?? true)
			{
				return _Errors.Values.SelectMany(x => x).ToList();
			}
			else
			{
				List<string> errors = new List<string>();
				foreach(string propertyName in propertyNameList)
				{
					if(_Errors.ContainsKey(propertyName))
					{
						errors.AddRange(_Errors[propertyName]);
					}
				}

				return errors;
			}
		}

		#region INotifyDataErrorInfo

		public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

		/// <summary>
		/// 获取属性错误列表（属性名传空则获取所有错误列表）
		/// </summary>
		/// <param name="propertyName">属性名</param>
		/// <returns>错误列表(List＜List＜string＞＞)</returns>
		public IEnumerable GetErrors(string propertyName)
		{
			if(string.IsNullOrEmpty(propertyName))
			{
				//Provide all the error collections.
				return _Errors.Values;
			}
			else
			{
				//Provice the error collection for the requested property (if it has errors).
				if(_Errors.ContainsKey(propertyName))
				{
					return _Errors[propertyName];
				}
				else
				{
					return null;
				}
			}
		}

		/// <summary>
		/// 整个类是否存在错误
		/// </summary>
		public bool HasErrors => _Errors.Count > 0;

		#endregion

		#region 实用方法(供参考)

		/// <summary>
		/// 验证是否为空
		/// </summary>
		/// <returns>true-不为空，false-为空</returns>
		public virtual bool ValidateBlank(object value, string errMsg = "", [CallerMemberName] string propertyName = null)
		{
			bool valid = !string.IsNullOrWhiteSpace(value + "");
			if(!valid) //为空；
			{
				if(string.IsNullOrWhiteSpace(errMsg))
				{
					errMsg = $"[{propertyName}] can't be blank";
				}

				SetErrors(propertyName, new List<string>() { errMsg });
			}
			else
			{
				ClearErrors(propertyName);
			}

			return valid;
		}

		#endregion

		#endregion

		#region 气泡弹窗命令

		private RelayCommand<List<object>> _ToastToWindowCmd;
		/// <summary>
		/// 气泡弹窗(到窗体)命令
		/// </summary>
		/// <example>
		/// <code>
		/// //Xaml中调用：
		/// &lt;Button Content="测试通过命令调用（窗体中间）" Command="{Binding ToastToWindowCmd}">
		///      &lt;Button.CommandParameter>
		///          &lt;MultiBinding Converter="{StaticResource MultiToListObjectConverter}">
		///              &lt;Binding Path="Content" RelativeSource="{RelativeSource Self}"/>
		///              &lt;Binding RelativeSource="{RelativeSource AncestorType=Window}"/>
		///          &lt;/MultiBinding>
		///      &lt;/Button.CommandParameter>
		///  &lt;/Button>
		/// </code>
		/// <code>
		/// //代码中调用：
		/// ToastToWindowCmd.Execute(new List&lt;object&gt; { "请先选择一项" });
		/// </code>
		/// </example>
		public RelayCommand<List<object>> ToastToWindowCmd => _ToastToWindowCmd ??= new RelayCommand<List<object>>(paras =>
		{
			Task.Run(delegate
			{
				string msg = string.Empty;
				if(paras.Any())
				{
					msg = paras[0] + "";
				}

				Window win = null;
				if(paras.Count >= 2)
				{
					win = paras[1] as Window;
				}

				Dispatcher.Invoke(delegate
				{
					Toast.Show(win, msg, new ToastOptions()
					{
						Icon = ToastIcons.Information,
						Location = ToastLocation.OwnerCenter,
						Time = 5000,
					});
				});
			});
		});

		private RelayCommand<string> _ToastToScreenCmd;
		/// <summary>
		/// 气泡弹窗(到屏幕)命令
		/// </summary>
		/// <example>
		/// <code>
		/// //Xaml中调用：
		/// &lt;Button Content="测试通过命令调用（屏幕中间）" Command="{Binding ToastToScreenCmd}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"/&gt;
		/// </code>
		/// <code>
		/// //代码中调用：
		/// ToastToScreenCmd.Execute("请先选择一项");
		/// </code>
		/// </example>
		public RelayCommand<string> ToastToScreenCmd => _ToastToScreenCmd ??= new RelayCommand<string>(msg =>
		{
			ToastToScreen(msg);
		});

		/// <summary>
		/// 气泡弹窗到屏幕中间的方法
		/// </summary>
		/// <param name="msg">消息内容</param>
		/// <param name="toastIcon">图标</param>
		/// <param name="millisecond">显示持续时间（毫秒）</param>
		public void ToastToScreen(string msg, ToastIcons toastIcon = ToastIcons.Information, int millisecond = 5000)
		{
			if(string.IsNullOrWhiteSpace(msg))
			{
				return;
			}

			Task.Run(delegate
			{
				Dispatcher.Invoke(delegate
				{
					Toast.Show(msg, new ToastOptions()
					{
						Icon = toastIcon,
						Location = ToastLocation.ScreenCenter,
						Time = millisecond,
					});
				});
			});
		}

		#endregion
	}
}
